/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

/**
 *
 * @author Administrateur
 */
public abstract class PlateauTablut {

    public static final byte VID = 3, EXIT = 6, TRONE = 4, INTERDITE = 4, DEHORS = 7, NR = 1, BLC = 0, ROI = 2, COMPLEMENT = NR + BLC;
    public static final int NOPION = -1, POSITION_TRONE = coord(4, 4);
    public static final int N = 11, Nx2 = N * 2, BORD_NE = 2, BORD_SW = 10,
            CMP = BORD_NE + BORD_SW, COINHG = coord(0, 0), COINBD = coord(8, 8), COINBG = coord(0, 8), COINHD = coord(8, 0);
    protected byte[] plateau;
    protected byte[] typeCase;
    protected byte[] positions;
    protected byte[] posPrise;
    protected int[] datePrise;
    protected int nbnoir, nbblanc; //modifié quand il y a prise/replacement
    public int date;
    protected PileDePile p;

    /**
     * le constructeur
     */
    public PlateauTablut() {
        init();
    }

    public void setP(PileDePile p) {
        this.p = p;
    }

    /**
     * initialise une nouvelle partie.
     */
    public void init() {
        date = 0;
        nbnoir = 16;
        nbblanc = 9;
        plateau = new byte[N * N];
        for(int i = plateau.length; --i>= 0;){plateau[i] = VID;}
        typeCase = new byte[N * N];
        posPrise = new byte[nbnoir + nbblanc+1];
        datePrise = new int[nbnoir + nbblanc+1];
        byte[] b = {
            coord(4, 4), coord(0, 0), coord(0, 0), //ROI en 0
            coord(0, 0), coord(0, 0), coord(0, 0),
            coord(0, 0), coord(0, 0), coord(0, 0), //fin blanc
            coord(0, 0), coord(0, 0), coord(0, 0), coord(0, 0),
            coord(0, 0), coord(0, 0), coord(0, 0), coord(0, 0),
            coord(0, 0), coord(0, 0), coord(0, 0), coord(0, 0),
            coord(0, 0), coord(0, 0), coord(0, 0), coord(0, 0) //fin noir
        };
        positions = b;

        plateau[positions[0]] = ROI;
        for (int i = 9 + nbnoir; --i > 9;) {
            plateau[positions[i]] = NR;
        }
        for (int i = 0 + nbblanc; --i > 0;) {
            plateau[positions[i]] = BLC;
        }
        for(int i = typeCase.length; --i>= 0;){typeCase[i] = DEHORS;}
        for(int i = 0; i<8; i++){
            for(int j = 0; j<8; j++){typeCase[coord(i, j)] = 0;}
        }
        typeCase[coord(4, 4)] = TRONE;

        typeCase[coord(3, 0)] = EXIT;
        typeCase[coord(4, 0)] = EXIT;
        typeCase[coord(5, 0)] = EXIT;

        typeCase[coord(3, 8)] = EXIT;
        typeCase[coord(4, 8)] = EXIT;
        typeCase[coord(5, 8)] = EXIT;

        typeCase[coord(8, 3)] = EXIT;
        typeCase[coord(8, 4)] = EXIT;
        typeCase[coord(8, 5)] = EXIT;

        typeCase[coord(0, 3)] = EXIT;
        typeCase[coord(0, 4)] = EXIT;
        typeCase[coord(0, 5)] = EXIT;

        typeCase[COINHG] = INTERDITE;
        typeCase[COINHD] = INTERDITE;
        typeCase[COINBG] = INTERDITE;
        typeCase[COINBD] = INTERDITE;

    }

    /**
     * bouge un pion (listé dans positions) de depart à arrive.
     * @param pion
     * @param depl
     */
    public void joue(byte pion, byte depart, byte arrive) {
        plateau[arrive] = plateau[depart];
        plateau[depart] = VID;
        positions[pion] = arrive;
        prisePion(arrive);
        date++;
    }
    /**
     * Revient sur le dernier coup joué. Ne pas remplir à la main:
     * appeler la fonction dejouerCoup de la pile à la place.
     * @param pion
     * @param depart
     * @param arrive
     */
    public void dejoue(byte pion, byte depart, byte arrive) {
        plateau[depart] = plateau[arrive];
        plateau[arrive] = VID;
        positions[pion] = depart;
        replacerPions();
        date--;
    }

    /**
     * @return Vrai sir partie est fini.
     */
    public boolean finDePartie(){
    return  VictoireBlanc() || VictoireNoir() || FinalCountDown();
    }

    /**
     * It's the final countdoown! tululu luu...
     * (Europe)
     * @return vrai si 30 tour sans prise se sont écoulés (finir la partie)
     */
    public final boolean FinalCountDown(){
        return (date - datePrise[nbnoir + nbblanc] >= 30);
    }

    /**
     * @return Vrai si noir a gagné.
     */
    public final boolean VictoireNoir() {

        if (date % 2 == NR) {
            int pos = positions[0];
            return ((plateau[pos + 1] == NR) || (typeCase[pos + 1] == TRONE))
                    && ((plateau[pos - 1] == NR) || (typeCase[pos - 1] == TRONE)
                    && ((plateau[pos - N] == NR) || (typeCase[pos - N] == TRONE))
                    && ((plateau[pos + N] == NR) || (typeCase[pos + N] == TRONE)));
        }
        return false;
    }
    /**
     *
     * @return Vrai si blanc à gagné
     */
    public final boolean VictoireBlanc() {
        return typeCase[positions[0]] == EXIT;
    }
    /**
     * Pas utilisé, se servir de VictoireNoir à la place.
     * @param kaboum
     *
    public final void priseRoi(byte kaboum){

        if( (date%2 == NR) && tuPeuxPasTest(kaboum, positions[0], 1)){
        int pos = positions[0];
           if(((plateau[pos+1] == NR)||(typeCase[pos+1] == DEHORS)||(typeCase[pos+1] == INTERDITE))
                    && ((plateau[pos-1] == NR)||(typeCase[pos-1] == DEHORS)||(typeCase[pos-1] == INTERDITE))
                    && ((plateau[pos-N] == NR)||(typeCase[pos-N] == DEHORS)||(typeCase[pos-N] == INTERDITE))
                    && ((plateau[pos+N] == NR)||(typeCase[pos+N] == DEHORS)||(typeCase[pos+N] == INTERDITE)))
           {
           //TODO: prise ROI effective
           }
        }


    }
    */
    /**
     * Replace les pions pris. Appelé par dejouer coup.
     */
    private final void replacerPions(){
        while(replacerPion()){}
    }
    private final boolean replacerPion(){
        int index = nbnoir + nbblanc;
        if(datePrise[index] == date){
            if(date%2 == NR){
                nbnoir++;
                positions[nbnoir] = posPrise[index];
                plateau[posPrise[index]] = NR;
            }else{
                nbblanc++;
                positions[nbblanc] = posPrise[index];
                plateau[posPrise[index]] = BLC;
            }
            return true;
        }
        return false;
    }

    /**
     * Effectue la prise. Appelé par prise Pion.
     * @param pos
     * @param couleurDuPris
     */
    private final void retirerPion(byte pos, int couleurDuPris){
        int index = nbnoir + nbblanc;

        datePrise[index] = date;
        posPrise[index] = pos;
        plateau[pos] = VID;
        if (couleurDuPris == NR){
            for(int i = 9 + nbnoir; --i>=10; ){
                if (positions[i] == pos){
                    bougeDansListe(nbnoir, pos);
                    nbnoir--;
                    break;
                }
            }
        }else{
            for(int i = nbblanc; --i>=1; ){
                if (positions[i] == pos){
                    bougeDansListe(nbblanc, pos);
                    nbblanc--;
                    break;
                }
            }
        }
    }

    /**
     * Joue un coup en particulier.
     * @param depart
     * @param arrive
     */
    public void jouerCeCoup(byte depart, byte arrive){
        plateau[arrive] = plateau[depart];
        plateau[depart] = VID;
        int pion = -1;


        if (date%2 == NR){
            for(int i = 9 + nbnoir; --i>=10; ){
                if (positions[i] == depart){
                    pion = i;
                    break;
                }
            }
        }else{
            for(int i = nbblanc; --i>=1; ){
                if (positions[i] == depart){
                    pion = i;
                    break;
                }
            }
        }


        positions[pion] = arrive;
        prisePion(arrive);
        date++;
    }
    /**
     *
     * @param kaboum : position du pion (hypothétiquement) preneur.
     */
    private final void prisePion(byte kaboum) {
        int predateur = date%2;
        int proie = COMPLEMENT - predateur;

        if (plateau[kaboum + 1] == proie) {
            if ((plateau[kaboum + 2] == predateur)
                    ||(typeCase[kaboum + 2] == TRONE)) {
                retirerPion( (byte)(kaboum+1) , proie);
            }
        }
        if (plateau[kaboum - 1] == proie) {
            if ((plateau[kaboum - 2] == predateur)
                    ||(typeCase[kaboum + 2] == TRONE)) {
                retirerPion( (byte)(kaboum-1) , proie);
            }
        }
        if (plateau[kaboum + N] == proie) {
            if ((plateau[kaboum + Nx2] == predateur)
                    ||(typeCase[kaboum + 2] == TRONE)) {
                retirerPion( (byte)(kaboum+N) , proie);
            }
        }
        if (plateau[kaboum - N] == proie) {
            if ((plateau[kaboum - Nx2] == predateur)
                    ||(typeCase[kaboum + 2] == TRONE)) {
                retirerPion((byte)(kaboum-N) , proie);
            }
        }

    }

    /**
     * Ca sert à déplacer l'info de la position d'un pions, lors d'une prise (écrasement du pion pris, à l'arrivé)
     * Attention, super sale. C'est pour ca qu'elle est private.
     * @param depart
     * @param arrive
     */
    private final void bougeDansListe(int depart, int arrive) {
        positions[arrive] = positions[depart];
    }

    /**
     * Calcul la distance de manhattan.
     * @param depart
     * @param arrive
     * @return
     */
    public static final int manhattan(int depart, int arrive) {
        int x, y;
        x = depart % N - arrive % N;
        y = depart / N - arrive / N;

        if (x * y < 0) {
            x = x - y;
        } else {
            x = x + y;
        }

        if (x < 0) {
            return -x;
        }
        return x;
    }

    /**
     * Convertisseur, de x [0;8] y [0;8] à ce qu'il faut. Crucial.
     * @param x
     * @param y
     * @return
     */
    public static final int coordInt(int x, int y) {
        return x + 1 + N * (y + 1);
    }
    /**
     * Convertisseur, de x [0;8] y [0;8] à ce qu'il faut. Crucial.
     * @param x
     * @param y
     * @return
     */
    public static final byte coord(int x, int y) {
        return (byte)(x + 1 + N * (y + 1));
    }
    /**
     * extrait l'abcisse
     * @param pos
     * @return
     */
    public static final int x(int pos){
        return pos % N - 1;
    }
    /**
     * extrait l'ordonnée
     * @param pos
     * @return
     */
    public static final int y(int pos){
        return pos / N - 1;
    }
    /**
     * Tout ca pour sauver un demi test. J'crois qu'j'l'utilise même pas.
     * @param depart
     * @param arrive
     * @param dist
     * @return
     *
 public static final boolean tuPeuxPasTest(int depart, int arrive, int dist) {
        int d = depart % N - arrive % N;
        int b = depart / N - arrive / N;
        if (d * b < 0) {
            d = d - b;
        } else {
            d = d + b;
        }
        return (dist == d) || (dist == -d);
    }
 */
 /**
  * Empile les mouvements légaux.
  */
 public void calcul_mvts(){
    if (date%2 == NR){
        for(int i =9+nbnoir; --i>= 10;  ){
            calcul_mvt((byte)i);
    }
    }else{
        for(int i = nbblanc+1; --i>= 0;  ){
            calcul_mvt((byte)i);
    }
    }
    }


 /**
  * calcul les mouvement d'un pion et empile les.
  * @param pos
  */
    private void calcul_mvt(byte pos) {
            byte tmp = positions[pos];
            //
            //si x inclu dans [1;3]U[5;7], alors axe y safe (pas de case interdite particulière)
            //
            if (tmp % N % 4 != BORD_NE) {
                for (int j = tmp + N; j <= COINBD; j += N) {
                    if (plateau[j] != VID) {
                        break;
                    }
                    p.addCoup(pos, tmp, (byte)j);
                }
                for (int j = tmp - N; j >= COINHG; j -= N) {
                    if (plateau[j] != VID) {
                        break;
                    }
                    p.addCoup(pos, tmp, (byte)j);
                }
            } else {
                //si x = 6 attention au trone
                if (tmp % N == 6) {
                    if (tmp > POSITION_TRONE) {

                        for (int j = tmp + N; j <= COINBD; j += N) {
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }
                        for (int j = tmp - N; j >= COINHG; j -= N) {
                            if (j == POSITION_TRONE) {
                                continue;
                            }
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }

                    } else {

                        for (int j = tmp + N; j <= COINBD; j += N) {
                            if (j == POSITION_TRONE) {
                                continue;
                            }
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }
                        for (int j = tmp - N; j >= COINHG; j -= N) {
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }

                    }

                } else { //attention aux coins
                    for (int j = tmp + N; j <= COINBG; j += N) {
                        if (plateau[j] != VID) {
                            break;
                        }
                        p.addCoup(pos, tmp, (byte)j);
                    }
                    for (int j = tmp - N; j >= COINHD; j -= N) {
                        if (plateau[j] != VID) {
                            break;
                        }
                        p.addCoup(pos, tmp, (byte)j);
                    }
                }

            }
            //si y inclu dans [1;3]U[5;7], alors axe x safe (pas de case interdite particulière)
            if (tmp / N % 4 != BORD_NE) {
                int k = tmp - tmp % N;
                for (int j = tmp; --j >= k;) {
                    if (plateau[j] != VID) {
                        break;
                    }
                    p.addCoup(pos, tmp, (byte)j);
                }
                k += N;
                for (int j = tmp + 1; j <= k; j++) {
                    if (plateau[j] != VID) {
                        break;
                    }
                    p.addCoup(pos, tmp, (byte)j);
                }

            } else {
                //si y = 6 attention au trone
                if (tmp / N == 6) {
                    if (tmp >= POSITION_TRONE) {

                        int k = tmp - tmp % N;
                        for (int j = tmp; --j >= k;) {
                            if (j == POSITION_TRONE) {
                                continue;
                            }
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }
                        k += N;
                        for (int j = tmp + 1; j <= k; j++) {
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }

                    } else {

                        int k = tmp - tmp % N;
                        for (int j = tmp; --j >= k;) {
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }
                        k += N;
                        for (int j = tmp + 1; j <= k; j++) {
                            if (j == POSITION_TRONE) {
                                continue;
                            }
                            if (plateau[j] != VID) {
                                break;
                            }
                            p.addCoup(pos, tmp, (byte)j);
                        }

                    }

                } else { //pas de trone, attention aux coins
                    int x = tmp % N;
                    int pseudoy = tmp - x;
                    for (int j = x; --j > 2;) {
                        if (plateau[pseudoy + j] != VID) {
                            break;
                        }
                        p.addCoup(pos, tmp, (byte)j);
                    }
                    for (int j = x + 1; j < 10; j++) {

                        if (plateau[pseudoy + j] != VID) {
                            break;
                        }
                        p.addCoup(pos, tmp, (byte)j);
                    }
                }
            }
    }

    /**
     * Une fonction qui affiche le plateau.
     * On peut s'en inspirer pour sortir un plateau au format voulu.
     */
    public void printPlateau(){
    char[] tab = new char[9];
    for(int i = 0; i<9; i++){
        for(int j = 0; j<9; j++){
            switch(plateau[coord(i,j)]){
                case 0: tab[j] = 'B';
                case 1: tab[j] = 'N';
                case 2: tab[j] = '¤';
                default: tab[j] = '-';
            }
        }
        System.out.println(tab);
    }
    }
    /* fonctions déjà has been.
    private final void bouge(int depart, int arrive) {
    plateau[arrive] = plateau[depart];
    plateau[depart] = VID;
    }
    private final void bouge(int Xdepart, int Ydepart, int Xarrive, int Yarrive) {
    this.bouge(Xdepart + 9 * Ydepart, Xarrive + 9 * Yarrive);
    }

    //*/
}
